package pt.compflow.graph;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;

import pt.ipp.isep.gecad.onto2flow.model.Edge;
import pt.ipp.isep.gecad.onto2flow.model.Node;

import com.hp.hpl.jena.rdf.model.Model;
import com.hp.hpl.jena.rdf.model.RDFList;
import com.hp.hpl.jena.rdf.model.RDFNode;
import com.hp.hpl.jena.util.iterator.ExtendedIterator;

public class Path extends ArrayList<Edge> {

	private static final long serialVersionUID = 1711253808504743444L;

	public Path() {
		super();
	}

	public Path(Collection<Edge> c) {
		super(c);
	}

	public Path(int initialCapacity) {
		super(initialCapacity);
	}

	public Path(RDFList list) {
		ExtendedIterator<RDFNode> it = list.iterator();
		while (it.hasNext()) {
			RDFNode el = it.next();
			if (!el.canAs(Edge.class)) {
				throw new IllegalArgumentException(el.toString() + " is not of type Edge");
			}
			add(el.as(Edge.class));
		}

	}

	public RDFList toRDFList(Model m) {
		return m.createList(iterator());
	}

	public boolean isContainedInList(List<Path> list) {
		for (Path p : list) {
			if (p.contains(this))
				return true;
		}
		return false;
	}

	public void removeContained(List<Path> list) {
		Iterator<Path> it = list.iterator();
		while (it.hasNext()) {
			Path p = it.next();
			if (contains(p))
				it.remove();
		}
	}

	public boolean removeFrom(Edge e) {
		int index = indexOf(e);
		if (index < 0)
			return false;
		int size = size();

		for (int i = index; i < size; i++) {
			remove(index);
		}
		return true;
	}

	public boolean contains(Node n) {
		if (n == null)
			return false;
		for (Edge e : this) {
			if (n.equals(e.getFromNodeNode()))
				return true;
			if (n.equals(e.getToNodeNode()))
				return true;
		}
		return false;
	}

	public boolean contains(Path p) {
		if (p.size() == 0 || size() < p.size())
			return false;

		int start = -1;
		for (int i = 0; i < size() && start < 0; i++) {
			if (equalEdges(get(i), p.get(0)))
				start = i;
		}

		if (start < 0 || (size() - start) < p.size())
			return false;

		for (int i = 0; i < p.size(); i++) {
			if (!equalEdges(get(start + i), p.get(i)))
				return false;
		}

		return true;
	}

	public boolean endsWith(Path p) {
		if (p.size() > size())
			return false;

		int j = size() - 1;
		for (int i = p.size() - 1; i >= 0; i--) {
			if (!equalEdges(get(j), p.get(i)))
				return false;
			j--;
		}

		return true;
	}

	@Override
	public Object clone() {
		return new Path(this);
	}

	@Override
	public boolean equals(Object o) {

		if (o == null || !(o instanceof List))
			return false;

		List<?> l = (List<?>) o;
		if (l.size() != size())
			return false;
		for (int i = 0; i < size(); i++) {
			if (!equalEdges((Edge) get(i), (Edge) l.get(i)))
				return false;
		}

		return true;
	}

	private boolean equalEdges(Edge e1, Edge e2) {
		return e1.getFromNodeNode().getElement().equals(e2.getFromNodeNode().getElement())
				&& e1.getToNodeNode().getElement().equals(e2.getToNodeNode().getElement())
				&& e1.getProperty().equals(e2.getProperty());
	}

	@Override
	public String toString() {
		StringBuilder builder = new StringBuilder();

		boolean first = true;
		for (Edge e : this) {
			if (first) {
				if (e.getFromNodeNode() != null)
					builder.append(e.getFromNodeNode().getElement().getLocalName());
				first = false;
			}
			if (e.getProperty() != null)
				builder.append(" --(" + e.getProperty().getLocalName() + ")--> ");
			builder.append(e.getToNodeNode().getElement().getLocalName());
		}

		return builder.toString();
	}

}
